﻿using System.Text;

namespace Sage.FileWriter
{
    /// <summary>
    /// 字符串文件写入器，支持链式调用
    /// </summary>
    public class StringFileWriter
    {
        #region 私有字段
        private string? _content;
        private string? _path;
        private string? _filename;
        private Encoding _encoding = Encoding.UTF8; // 默认 UTF-8 编码
        private Func<string, FileExistsAction>? _onFileExists;
        private Action<long, long, double>? _onProgress;
        private bool _enableProgress;
        #endregion

        #region 构造函数
        private StringFileWriter() { }
        #endregion

        #region 链式调用方法
        /// <summary>
        /// 设置要写入的字符串内容（默认UTF-8编码）
        /// </summary>
        /// <param name="content">字符串内容</param>
        /// <returns>StringFileWriter实例</returns>
        /// <exception cref="ArgumentException">内容为空时抛出异常</exception>
        public static StringFileWriter Content(string content)
        {
            if (string.IsNullOrWhiteSpace(content))
                throw new ArgumentException("Content cannot be null, empty, or whitespace.", nameof(content));
            return new StringFileWriter { _content = content, _encoding = Encoding.UTF8 };
        }

        /// <summary>
        /// 设置要写入的字符串内容和编码
        /// </summary>
        /// <param name="content">字符串内容</param>
        /// <param name="encoding">编码类型</param>
        /// <returns>StringFileWriter实例</returns>
        /// <exception cref="ArgumentException">内容为空时抛出异常</exception>
        public static StringFileWriter Content(string content, FileEncoding encoding)
        {
            if (string.IsNullOrWhiteSpace(content))
                throw new ArgumentException("Content cannot be null, empty, or whitespace.", nameof(content));

            return new StringFileWriter
            {
                _content = content,
                _encoding = GetSystemEncoding(encoding)
            };
        }

        /// <summary>
        /// 设置文件路径，可包含或不包含文件名
        /// </summary>
        /// <param name="path">文件路径</param>
        /// <returns>StringFileWriter实例</returns>
        /// <exception cref="ArgumentException">路径为空时抛出异常</exception>
        public StringFileWriter Path(string path)
        {
            if (string.IsNullOrWhiteSpace(path))
                throw new ArgumentException("Path cannot be null, empty, or whitespace.", nameof(path));
            _path = path;
            return this;
        }

        /// <summary>
        /// 设置文件名，包含扩展名，会覆盖Path中的文件名部分
        /// </summary>
        /// <param name="filename">文件名</param>
        /// <returns>StringFileWriter实例</returns>
        /// <exception cref="ArgumentException">文件名为空时抛出异常</exception>
        public StringFileWriter Filename(string filename)
        {
            if (string.IsNullOrWhiteSpace(filename))
                throw new ArgumentException("Filename cannot be null, empty, or whitespace.", nameof(filename));
            _filename = filename;
            return this;
        }

        /// <summary>
        /// 设置文件存在时的回调处理
        /// </summary>
        /// <param name="callback">回调函数，接收文件路径，返回处理策略</param>
        /// <returns>StringFileWriter实例</returns>
        public StringFileWriter OnFileExists(Func<string, FileExistsAction> callback)
        {
            _onFileExists = callback;
            return this;
        }

        /// <summary>
        /// 设置进度回调
        /// </summary>
        /// <param name="callback">进度回调，参数为已写入字节数、总字节数、百分比</param>
        /// <returns>StringFileWriter实例</returns>
        public StringFileWriter OnProgress(Action<long, long, double> callback)
        {
            _onProgress = callback;
            _enableProgress = callback != null;
            return this;
        }

        /// <summary>
        /// 启用或禁用进度报告
        /// </summary>
        /// <param name="enable">是否启用</param>
        /// <returns>StringFileWriter实例</returns>
        public StringFileWriter EnableProgress(bool enable = true)
        {
            _enableProgress = enable;
            return this;
        }
        #endregion

        #region 保存方法
        /// <summary>
        /// 异步保存文件
        /// </summary>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>文件写入结果</returns>
        public async Task<FileWriteResult> SaveAsync(CancellationToken cancellationToken = default)
        {
            // 快速参数验证在调用线程执行
            var validationResult = ValidateParameters();
            if (!validationResult.Success)
                return validationResult;
            try
            {
                return await WriteFileAsync(cancellationToken).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                return new FileWriteResult
                {
                    Success = false,
                    ErrorMessage = "File write operation was cancelled."
                };
            }
            catch (Exception ex)
            {
                return new FileWriteResult
                {
                    Success = false,
                    ErrorMessage = $"Unexpected error: {ex.Message}"
                };
            }
        }

        /// <summary>
        /// 同步保存文件
        /// </summary>
        /// <returns>文件写入结果</returns>
        public FileWriteResult Save()
        {
            if (SynchronizationContext.Current != null)
            {
                // 在有同步上下文时，使用 Task.Run 避免死锁
                return Task.Run(async () => await SaveAsync().ConfigureAwait(false))
                          .GetAwaiter()
                          .GetResult();
            }
            else
            {
                // 没有同步上下文时，直接调用异步版本
                return SaveAsync().GetAwaiter().GetResult();
            }
        }
        #endregion

        #region 私有方法
        /// <summary>
        /// 将FileEncoding枚举转换为System.Text.Encoding
        /// </summary>
        /// <param name="fileEncoding">文件编码枚举</param>
        /// <returns>System.Text.Encoding实例</returns>
        private static Encoding GetSystemEncoding(FileEncoding fileEncoding)
        {
            return fileEncoding switch
            {
                FileEncoding.UTF8 => new UTF8Encoding(false), // 无BOM
                FileEncoding.UTF8WithBOM => new UTF8Encoding(true), // 带BOM
                FileEncoding.UTF16 => new UnicodeEncoding(false, true), // 小端序，带BOM
                FileEncoding.UTF32 => new UTF32Encoding(false, true), // 小端序，带BOM
                FileEncoding.ASCII => Encoding.ASCII,
                FileEncoding.GB2312 => Encoding.GetEncoding("gb2312"),
                FileEncoding.GBK => Encoding.GetEncoding("gbk"),
                FileEncoding.Big5 => Encoding.GetEncoding("big5"),
                _ => new UTF8Encoding(false) // 默认UTF-8
            };
        }

        /// <summary>
        /// 验证参数是否有效
        /// </summary>
        /// <returns>验证结果</returns>
        private FileWriteResult ValidateParameters()
        {
            if (string.IsNullOrWhiteSpace(_content))
            {
                return new FileWriteResult
                {
                    Success = false,
                    ErrorMessage = "Content is required. Call Content() method first."
                };
            }
            if (string.IsNullOrWhiteSpace(_path))
            {
                return new FileWriteResult
                {
                    Success = false,
                    ErrorMessage = "Path is required. Call Path() method first."
                };
            }
            // 检查是否有文件名
            var finalPath = GetFinalFilePath();
            if (string.IsNullOrWhiteSpace(System.IO.Path.GetFileName(finalPath)))
            {
                return new FileWriteResult
                {
                    Success = false,
                    ErrorMessage = "No filename specified. Either include filename in Path() or call Filename() method."
                };
            }
            return new FileWriteResult { Success = true };
        }

        /// <summary>
        /// 获取最终的文件路径
        /// </summary>
        /// <returns>最终文件路径</returns>
        private string GetFinalFilePath()
        {
            if (!string.IsNullOrWhiteSpace(_filename))
            {
                string directory;

                // 优先检查是否为已存在的目录
                if (Directory.Exists(_path))
                {
                    directory = _path!;
                }
                // 检查是否包含文件扩展名
                else if (System.IO.Path.HasExtension(_path))
                {
                    // 有扩展名，说明_path包含文件名，提取目录部分
                    directory = System.IO.Path.GetDirectoryName(_path) ?? _path!;
                }
                else
                {
                    // 没有扩展名，当作目录处理
                    directory = _path!;
                }
                return System.IO.Path.Combine(directory, _filename);
            }
            return _path ?? string.Empty;
        }

        /// <summary>
        /// 异步写入文件
        /// </summary>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>文件写入结果</returns>
        private async Task<FileWriteResult> WriteFileAsync(CancellationToken cancellationToken)
        {
            var finalPath = GetFinalFilePath();

            try
            {
                // 确保目录存在
                var directory = System.IO.Path.GetDirectoryName(finalPath);
                if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }
                // 处理文件已存在的情况
                var processedPath = await HandleFileExistsAsync(finalPath, cancellationToken).ConfigureAwait(false);
                if (string.IsNullOrEmpty(processedPath))
                {
                    return new FileWriteResult
                    {
                        Success = false,
                        ErrorMessage = "File write skipped by user choice."
                    };
                }
                finalPath = processedPath;

                // 使用指定编码转换为字节数组
                var bytes = _encoding.GetBytes(_content!);

                // 写入文件
                if (_enableProgress && _onProgress != null)
                {
                    await WriteFileWithProgressAsync(finalPath, bytes, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    await File.WriteAllBytesAsync(finalPath, bytes, cancellationToken).ConfigureAwait(false);
                }
                return new FileWriteResult
                {
                    Success = true,
                    FinalFilePath = finalPath,
                    FileSize = bytes.Length,
                    Encoding = _encoding.EncodingName // 添加编码信息
                };
            }
            catch (UnauthorizedAccessException ex)
            {
                return new FileWriteResult
                {
                    Success = false,
                    ErrorMessage = $"Access denied: {ex.Message}"
                };
            }
            catch (DirectoryNotFoundException ex)
            {
                return new FileWriteResult
                {
                    Success = false,
                    ErrorMessage = $"Directory not found: {ex.Message}"
                };
            }
            catch (IOException ex)
            {
                return new FileWriteResult
                {
                    Success = false,
                    ErrorMessage = $"IO error: {ex.Message}"
                };
            }
        }

        /// <summary>
        /// 处理文件已存在的情况
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>处理后的文件路径，如果跳过则返回null</returns>
        private async Task<string?> HandleFileExistsAsync(string filePath, CancellationToken cancellationToken)
        {
            // 异步检查文件是否存在
            bool fileExists = await Task.Run(() => File.Exists(filePath), cancellationToken).ConfigureAwait(false);

            if (!fileExists)
            {
                return filePath;
            }
            if (_onFileExists == null)
            {
                // 默认覆盖
                return filePath;
            }
            var action = _onFileExists(filePath);

            return action switch
            {
                FileExistsAction.Overwrite => filePath,
                FileExistsAction.Skip => null,
                FileExistsAction.Rename => GenerateUniqueFileName(filePath),
                FileExistsAction.ThrowError => throw new InvalidOperationException($"File already exists: {filePath}"),
                _ => filePath
            };
        }

        /// <summary>
        /// 带进度报告的异步文件写入
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="data">要写入的数据</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>异步任务</returns>
        private async Task WriteFileWithProgressAsync(string filePath, byte[] data, CancellationToken cancellationToken)
        {
            const int bufferSize = 8192; // 8KB 缓冲区
            var totalBytes = data.Length;
            var bytesWritten = 0;
            using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write,
                                                 FileShare.None, bufferSize, useAsync: true);

            while (bytesWritten < totalBytes)
            {
                cancellationToken.ThrowIfCancellationRequested();

                var bytesToWrite = Math.Min(bufferSize, totalBytes - bytesWritten);
                await fileStream.WriteAsync(data.AsMemory(bytesWritten, bytesToWrite), cancellationToken).ConfigureAwait(false);
                bytesWritten += bytesToWrite;

                var percentage = (double)bytesWritten / totalBytes * 100;
                _onProgress?.Invoke(bytesWritten, totalBytes, percentage);
            }

            await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false);
        }

        /// <summary>
        /// 生成唯一的文件名
        /// </summary>
        /// <param name="originalPath">原始文件路径</param>
        /// <returns>唯一的文件路径</returns>
        private static string GenerateUniqueFileName(string originalPath)
        {
            var directory = System.IO.Path.GetDirectoryName(originalPath);
            var fileNameWithoutExt = System.IO.Path.GetFileNameWithoutExtension(originalPath);
            var extension = System.IO.Path.GetExtension(originalPath);

            int counter = 1;
            string newPath;

            do
            {
                var newFileName = $"{fileNameWithoutExt}_{counter}{extension}";
                newPath = System.IO.Path.Combine(directory ?? string.Empty, newFileName);
                counter++;
            }
            while (File.Exists(newPath));

            return newPath;
        }
        #endregion
    }
}
